import Glibc

class Timer {
    private let CLOCK_REALTIME = 0
    private var start_timespec = timespec()
    private var end_timespec = timespec()
    private var time_spec = timespec()
    func start() {
        clock_gettime(Int32(CLOCK_REALTIME),&start_timespec)
    }
    
    func stop() -> Double {
        clock_gettime(Int32(CLOCK_REALTIME),&end_timespec)
        let start_time = Double(start_timespec.tv_sec * 1_000_000 + start_timespec.tv_nsec / 1_000)
        let end_time = Double(end_timespec.tv_sec * 1_000_000 + end_timespec.tv_nsec / 1_000)
        let time = end_time - start_time
        return time / 1_000
    }
    func getTime() -> Double {
        clock_gettime(Int32(CLOCK_REALTIME),&time_spec)
        return Double(time_spec.tv_sec * 1_000_000 + time_spec.tv_nsec / 1_000)
    }
}

let timer = Timer()

typealias AminoAcid = (prob: Double, sym: UInt8)

let IM = 139968
let IA = 3877
let IC = 29573
var seed = 42

let width = 60

var homosapiens = [
    AminoAcid(0.3029549426680, 97), // "a"),
    AminoAcid(0.1979883004921, 99), // "c"),
    AminoAcid(0.1975473066391, 103), // "g"),
    AminoAcid(0.3015094502008, 116), // "t"),
]

func search(rnd: Double, within arr: [AminoAcid]) -> UInt8 {
    var low = 0
    var high = arr.count - 1
    while low <= high {
        let mid = (high + low) >> 1
        if arr[mid].prob >= rnd {
            high = mid - 1
        } else {
            low = mid + 1
        }
    }
    return arr[high+1].sym
}

func accumulateProbabilities( acid: inout [AminoAcid]) {
    for i in 1..<acid.count {
        acid[i].prob += acid[i-1].prob
    }
}

func randomFasta(buffer: inout [Int], acid: inout [AminoAcid], _ n: Int) {
    var cnt = n
    accumulateProbabilities(acid: &acid)
    var pos = 0
    while cnt > 0 {
        var m = cnt
        if m > width {
            m = width
        }
        let f = 1.0 / Double(IM)
        var myrand = seed
        for _ in 0..<m {
            myrand = (myrand * IA + IC) % IM
            let r = Double(myrand) * f
            buffer[pos] = Int(search(rnd: r, within: acid))
            pos += 1
            if pos == buffer.count {
                pos = 0
            }
        }
        seed = myrand
        buffer[pos] = 10
        pos += 1
        if pos == buffer.count {
            pos = 0
        }
        cnt -= m
    }
}

func runFasta3()->Int{
    let bufferSize = 256 * 1024
    var buffer = [Int](repeating: 10, count: bufferSize)
    let n: Int = 250000
    // alu.popLast()
    timer.start()
    randomFasta(buffer: &buffer, acid: &homosapiens,5*n)
    let time = timer.stop()
    print("Array Access - RunFastaRandom2:\t" + String(time) + "\tms");
    return  Int(time) 
}
_ = runFasta3()
